
using namespace std;

#include <iostream>
#include <cstdlib>
#include <unistd.h>    // usleep
#include <fcntl.h>     // threads
#include <pthread.h>
#include <string>      // messages
#include <queue>       // the message queue


// Global data
// -----------

pthread_mutex_t myMutex = PTHREAD_MUTEX_INITIALIZER;
queue<string> myMessageQueue;


// Checks if termination is required
// ---------------------------------

const bool isExitMessage(const string theMessage)
{
    return ("exit" == theMessage);
}


// Function to receive data from the message queue (is executed as thread)
// -----------------------------------------------

void *receiveMessage(void *arg)
{
    long queueSize;
    string nextMessage;

    while (true)
    {
        // Test whether container is empty
        if (true == myMessageQueue.empty())
        {
            usleep(10000); // sleep 0.01 sec before trying again
            continue;
        }

        // we end up here because there was something in the msg queue
        pthread_mutex_lock(&myMutex);

        queueSize = myMessageQueue.size();

        if (queueSize > 5)
        {
            cout << "Queue size: " << queueSize << endl;
        }

        nextMessage = myMessageQueue.front();    // Access next element
        myMessageQueue.pop();                    // Delete next element

        cout << "\tReceive: " << nextMessage << endl;

        pthread_mutex_unlock(&myMutex);


        if (true == isExitMessage(nextMessage))
        {
            pthread_exit((void *)0);
        }

    }

    pthread_exit((void *)0);
}


// Function to transmit data into message queue. Reads from stdin. (is executed as thread)
// ---------------------------------------------------------------

void *transmitMessage(void *arg)
{
    string nextMessage;

    while (true)
    {
        cin >> nextMessage;

        pthread_mutex_lock(&myMutex);

        myMessageQueue.push(nextMessage);    // Insert element
        cout << "Transmit: " << nextMessage << endl;

        pthread_mutex_unlock(&myMutex);

        if (true == isExitMessage(nextMessage))
        {
            pthread_exit((void *)0);
        }
    }

    pthread_exit((void *)0);
}


// Main function (creates the threads and returns)
// -------------

int main (int argc, char **argv)
{
    pthread_t receiveThread;
    pthread_t transmitThread;

    // Create threads
    if (pthread_create(&receiveThread, NULL, receiveMessage, NULL))
    {
        cout << argv[0] << " cannot create receive thread\n";
        exit(1);
    }
    if (pthread_create(&transmitThread, NULL, transmitMessage, NULL))
    {
        cout << argv[0] << " cannot create transmit thread\n";
        exit(1);
    }


    // At this point the main thread can perform its actions or end
    cout << "Main thread ends...        Receive and transmit threads still running!\n";
    cout << "Please type and data...    'exit' terminates the the threads!\n";

    pthread_exit((void *)0);
}

